package it.uniroma3.dia.predictors;

import it.uniroma3.dia.db.DBDatasource;
import it.uniroma3.dia.models.RatingMovie;
import it.uniroma3.dia.utils.Utils;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;

/** 
* @author Daniele D'Andrea
* @author Antonio Gallo
*/
public abstract class AbstractPredictor implements Runnable {

	protected Connection connection;
	
	protected double errorMAE;
	protected double errorRMSE;
	protected double errorGreaterThanN;
	protected int processedRating;
	
	protected ConcurrentLinkedQueue<RatingMovie> concurredLinkedQueue;
	protected CountDownLatch countDownLatch;
	
	public AbstractPredictor(){
		this.errorMAE = 0;
		this.errorRMSE = 0;
		this.errorGreaterThanN = 0;
	}
	
	public void setConcurredLinkedQueue(
			ConcurrentLinkedQueue<RatingMovie> concurredLinkedQueue) {
		this.concurredLinkedQueue = concurredLinkedQueue;
	}
	
	public void setCountDownLatch(CountDownLatch countDownLatch) {
		this.countDownLatch = countDownLatch;
	}

	public int getProcessedRating() {
		return processedRating;
	}

	public double getErrorMAE() {
		return errorMAE;
	}

	public double getErrorRMSE() {
		return errorRMSE;
	}
	
	public double getErrorGreaterThanN() {
		return errorGreaterThanN;
	}

	public void setErrorGreaterThanN(double errorGreaterThanN) {
		this.errorGreaterThanN = errorGreaterThanN;
	}

	protected void init() throws Exception{
		connection = new DBDatasource().getConnection();
	}
	
	public abstract double ratingPrediction(int userID, int movieID, long timestamp) throws SQLException;

	public void updateErrors(int userID, int movieID, double prediction, double real){
		double errorPrediction = Math.abs(prediction - real);
		
		errorMAE+=errorPrediction;
		errorRMSE+=Math.pow(errorPrediction, 2);

		if(Math.abs(prediction-real)>1.5)
			errorGreaterThanN++;
	}
	
	public void run(){
		processedRating = 0;
		try{
			this.init();
			
			while(concurredLinkedQueue.size()>0){
				RatingMovie ratingMovie = concurredLinkedQueue.poll();
				try {
					int userID = ratingMovie.getUserID();
					int movieID = ratingMovie.getMovieID();
					long timestamp = ratingMovie.getTimestamp();
					
					double real = Utils.realUserRating(connection, userID, movieID);
					
					double prediction = real;
					
					if(real<=0){
						prediction = this.ratingPrediction(userID, movieID, timestamp);
					}
						
					ratingMovie.setPrediction(prediction);
					
					//this.updateErrors(userID, movieID, prediction, real);
					processedRating++;
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			connection.close();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		
		countDownLatch.countDown();
	}
}
